using System;
using System.Reflection;
using Needle.Utils;

namespace Needle.Core.Injection
{

    /// <summary>
    /// Attribute to indicate injection sites on types.
    /// </summary>
    [AttributeUsage(AttributeTargets.Constructor
                    | AttributeTargets.Property
                    | AttributeTargets.Method
                    | AttributeTargets.Parameter,
                    AllowMultiple = false,
                    Inherited = true)]
    public class InjectAttribute : Attribute
    {

        #region static members

        private const Type DefaultType = null;
        private const string DefaultName = null;
        private const int DefaultOrder = 0;

        /// <summary>
        /// returns the inject attribute delared on the member, or null if there is none.
        /// </summary>
        public static InjectAttribute GetFrom(MemberInfo memberInfo)
        {
            Ensure.NotNull(memberInfo, "memberInfo");
            return (InjectAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(InjectAttribute), true);
        }

        /// <summary>
        /// returns the inject attribute delared on the member, or null if there is none.
        /// </summary>
        public static InjectAttribute GetFrom(ParameterInfo parameterInfo)
        {
            Ensure.NotNull(parameterInfo, "parameterInfo");
            return (InjectAttribute)Attribute.GetCustomAttribute(parameterInfo, typeof(InjectAttribute), true);
        }

        /// <summary>
        /// returns the inject attributes delared on the member, or null if there is none.
        /// </summary>
        public static InjectAttribute[] GetAllFrom(MemberInfo memberInfo)
        {
            Ensure.NotNull(memberInfo, "memberInfo");
            InjectAttribute[] attrs;
            attrs = (InjectAttribute[])Attribute.GetCustomAttributes(memberInfo, typeof(InjectAttribute), true);
            return attrs;
        }

        /// <summary>
        /// returns the inject attributes delared on the member, or null if there is none.
        /// </summary>
        public static InjectAttribute[] GetAllFrom(ParameterInfo parameterInfo)
        {
            Ensure.NotNull(parameterInfo, "parameterInfo");
            InjectAttribute[] attrs;
            attrs = (InjectAttribute[])Attribute.GetCustomAttributes(parameterInfo, typeof(InjectAttribute), true);
            return attrs;
        }

        #endregion

        #region constructors
        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute()
            : this(DefaultType, DefaultName, DefaultOrder)
        {
        }

        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(Type t)
            : this(t, DefaultName, DefaultOrder)
        {
        }

        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(Type t, string name)
            : this(t, name, DefaultOrder)
        {
        }

        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(Type t, string name, int order)
        {
            Type = t;
            Name = name;
            Order = order;
        }

        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(string name)
        {
            Type = DefaultType;
            Name = name;
            Order = DefaultOrder;
        }

        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(string name, int order)
        {
            Type = DefaultType;
            Name = name;
            Order = order;
        }
        /// <summary>
        /// Initializes a new Inject attribute
        /// </summary>
        public InjectAttribute(int order)
        {
            Type = DefaultType;
            Name = DefaultName;
            Order = order;
        }
        #endregion

        #region Properties

        private string m_Name;
        /// <summary>
        /// A default dependency name if applied to a method or ctor,
        /// or the dependency name if applied to a parameter or property.
        /// </summary>
        public string Name
        {
            get { return m_Name; }
            set
            {
                if (value == String.Empty)
                {
                    throw new ArgumentException("Value cannot be the empty string, use null if no name was intended.", value);
                }
                m_Name = value;
            }
        }

        private Type m_Type;
        /// <summary>
        /// The dependency type if applied to a member with only 1 parameter, 
        /// value is ignored for members with more than 1 parameter.
        /// Apply atrtibute with the Type set to parameters directly for members with more than 1 parameter
        /// </summary>
        public Type Type
        {
            get { return m_Type; }
            set { m_Type = value; }
        }

        private int m_Order;
        /// <summary>
        /// The injection order if applied to a property or method, ignored otherwise.
        /// Ordering is not specified for members which have the same order.
        /// Must be non-negative.
        /// </summary>
        public int Order
        {
            get { return m_Order; }
            set
            {
                Ensure.GreaterThan(-1, value, "value");
                m_Order = value;
            }
        }

        #endregion

        #region overridden methods

        /// <summary>
        /// True if the attribute has only default values.
        /// </summary>
        public override bool IsDefaultAttribute()
        {
            return m_Type == DefaultType
                && m_Name == DefaultName
                && m_Order == DefaultOrder;
        }

        #endregion

    }

}
